"""
Objects representing PDF path (stroke and filling) extracted by ``page.get_drawings()``.
表示由``page.get_drawings()``提取的PDF路径(描边和填充)的对象。

This method is new since ``PyMuPDF`` 1.18.0, with both pdf raw path and annotations like Line,
Square and Highlight considered.
此方法自``PyMuPDF`` 1.18.0版本起新增，同时考虑了PDF原始路径和Line、Square、Highlight等注释。

* https://pymupdf.readthedocs.io/en/latest/page.html#Page.get_drawings
* https://pymupdf.readthedocs.io/en/latest/faq.html#extracting-drawings
"""

import fitz
from ..image.ImagesExtractor import ImagesExtractor
from ..common.share import lazyproperty
from ..common.Collection import Collection
from .Path import Path


class Paths(Collection):
    """A collection of paths."""

    """路径的集合。"""

    def restore(self, raws: list):
        """Initialize paths from raw data get by ``page.get_drawings()``."""
        """从``page.get_drawings()``获取的原始数据初始化路径。"""
        rect = (0, 0, self.parent.width, self.parent.height)
        for raw in raws:
            path = Path(raw)
            # ignore path out of page
            # 忽略页面外的路径
            if not path.bbox.intersects(rect):
                continue
            self.append(path)

        return self

    @lazyproperty
    def bbox(self):
        bbox = fitz.Rect()
        for instance in self._instances:
            bbox |= instance.bbox
        return bbox

    @property
    def is_iso_oriented(self):
        """It is iso-oriented when all contained segments are iso-oriented."""
        """当所有包含的线段都是等方向的(水平或垂直)时，它是等方向的。"""
        for instance in self._instances:
            if not instance.is_iso_oriented:
                return False
        return True

    def plot(self, page):
        """Plot paths for debug purpose.
        为调试目的绘制路径。

        Args:
            page (fitz.Page): ``PyMuPDF`` page.
            page (fitz.Page): ``PyMuPDF``页面。
        """
        if not self._instances:
            return
        # make a drawing canvas and plot path
        # 创建绘图画布并绘制路径
        canvas = page.new_shape()
        for path in self._instances:
            path.plot(canvas)
        canvas.commit()  # commit the drawing shapes to page
        # 将绘制的形状提交到页面

    def to_shapes(self):
        """Convert contained paths to ISO strokes or rectangular fills.
        将包含的路径转换为ISO描边或矩形填充。

        Returns:
            list: A list of ``Shape`` raw dicts.
            list: 一个包含``Shape``原始字典的列表。
        """
        shapes = []
        for path in self._instances:
            # consider iso-oriented path only
            # 只考虑等方向的路径
            if not path.is_iso_oriented:
                continue
            shapes.extend(path.to_shapes())
        return shapes

    def to_shapes_and_images(
        self,
        min_svg_gap_dx: float = 15,
        min_svg_gap_dy: float = 15,
        min_w: float = 2,
        min_h: float = 2,
        clip_image_res_ratio: float = 3.0,
    ):
        """Convert paths to iso-oriented shapes or images. The semantic type of path is either
        table/text style or vector graphic. This method is to:
        将路径转换为等方向形状或图像。路径的语义类型可以是表格/文本样式或矢量图形。此方法旨在：
        * detect svg regions -> exist at least one non-iso-oriented path
        * 检测svg区域 -> 至少存在一个非等方向路径
        * convert svg to bitmap by clipping page
        * 通过裁剪页面将svg转换为位图
        * convert the rest paths to iso-oriented shapes for further table/text style parsing
        * 将其余路径转换为等方向形状，以便进一步解析表格/文本样式

        Args:
            min_svg_gap_dx (float): Merge svg if the horizontal gap is less than this value.
            min_svg_gap_dx (float): 如果水平间隙小于此值，则合并svg。
            min_svg_gap_dy (float): Merge svg if the vertical gap is less than this value.
            min_svg_gap_dy (float): 如果垂直间隙小于此值，则合并svg。
            min_w (float): Ignore contours if the bbox width is less than this value.
            min_w (float): 如果边界框宽度小于此值，则忽略轮廓。
            min_h (float): Ignore contours if the bbox height is less than this value.
            min_h (float): 如果边界框高度小于此值，则忽略轮廓。
            clip_image_res_ratio (float, optional): Resolution ratio of clipped bitmap.
                Defaults to 3.0.
            clip_image_res_ratio (float, optional): 裁剪位图的分辨率比率。
                默认为3.0。

        Returns:
            tuple: (list of shape raw dict, list of image raw dict).
            tuple: (形状原始字典列表，图像原始字典列表)。
        """
        # convert all paths to shapes if no non-iso-orientated path exists
        # 如果不存在非等方向路径，则将所有路径转换为形状
        iso_shapes = []
        if self.is_iso_oriented:
            iso_shapes.extend(self.to_shapes())
            return iso_shapes, []

        # detect svg with python opencv
        # 使用python opencv检测svg
        images = []
        ie = ImagesExtractor(self.parent.page_engine)
        groups = ie.detect_svg_contours(min_svg_gap_dx, min_svg_gap_dy, min_w, min_h)

        # `bbox` is the external bbox of current region, while `inner_bboxes` are the inner contours
        # of level-2 hierarchy, i.e. contours under table cell.
        # `bbox`是当前区域的外部边界框，而`inner_bboxes`是二级层次结构的内部轮廓，
        # 即表格单元格下的轮廓。
        # * it a table (or text style) if paths contained in `bbox` but excluded from `inner_bboxes`
        #   are all iso-oriented -> export iso-shapes, clip page image based on `inner_bboxes`;
        # * 如果包含在`bbox`中但从`inner_bboxes`中排除的路径都是等方向的，那么它是表格（或文本样式）
        #   -> 导出等方向形状，根据`inner_bboxes`裁剪页面图像；
        # * otherwise, it's a vector graphic -> clip page image (without any text) based on `bbox`
        # * 否则，它是矢量图形 -> 根据`bbox`裁剪页面图像（不含任何文本）
        def contained_in_inner_contours(path: Path, contours: list):
            for bbox in contours:
                if fitz.Rect(bbox).contains(path.bbox):
                    return True
            return False

        # group every path to one of the detected bbox
        # 将每个路径分组到检测到的边界框之一
        group_paths = [Paths() for _ in groups]  # type: list[Paths]
        for path in self._instances:
            for (bbox, inner_bboxes), paths in zip(groups, group_paths):
                if path.bbox.intersects(bbox):
                    if not contained_in_inner_contours(path, inner_bboxes):
                        paths.append(path)
                    break

        # check each group
        # 检查每个组
        for (bbox, inner_bboxes), paths in zip(groups, group_paths):
            # all iso-oriented paths -> it's a table, but might contain svg in cell as well
            # 所有等方向路径 -> 它是一个表格，但单元格中可能也包含svg
            if paths.is_iso_oriented:
                iso_shapes.extend(paths.to_shapes())
                for svg_bbox in inner_bboxes:
                    images.append(
                        ie.clip_page_to_dict(
                            bbox=fitz.Rect(svg_bbox),
                            rm_image=True,
                            clip_image_res_ratio=clip_image_res_ratio,
                        )
                    )

            # otherwise, it's a svg
            # 否则，它是一个svg
            else:
                images.append(
                    ie.clip_page_to_dict(
                        bbox=fitz.Rect(bbox),
                        rm_image=True,
                        clip_image_res_ratio=clip_image_res_ratio,
                    )
                )

        return iso_shapes, images
